/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package calculadorfpa.persistence;

import calculadorfpa.connection.DBUtils;
import calculadorfpa.modelo.ArquivoInterfaceExterna;
import calculadorfpa.modelo.ArquivoLogicoInterno;
import calculadorfpa.modelo.CaracteristicaGeral;
import calculadorfpa.modelo.ConsultaExterna;
import calculadorfpa.modelo.EntradaExterna;
import calculadorfpa.modelo.Funcao;
import calculadorfpa.modelo.FuncaoDados;
import calculadorfpa.modelo.FuncaoTransacional;
import calculadorfpa.modelo.NivelInfluencia;
import calculadorfpa.modelo.Projeto;
import calculadorfpa.modelo.SaidaExterna;
import calculadorfpa.util.EnumerationUtil;
import calculadorfpa.util.MyFPAUtil;
import com.mysql.jdbc.Statement;
import java.sql.PreparedStatement;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * Implementação das operações e consultas ao banco de dados.
 * @author TECBMEPI
 */
public class Persistence {

    // Inserts
    private static final String queryInsertProjeto = "INSERT INTO Projeto (idProjeto, nmProjeto, pontoFuncao) VALUES (?, ?, ?)";
    private static final String queryInsertProjetoHasCaracteristica = "INSERT INTO ProjetoHasCaracteristica (idProjeto, idCaracteristicaGeral, nivelInfluencia) VALUES (?, ?, ?)";
    private static final String queryInsertFuncao = "INSERT INTO Funcao (idFuncao, idProjeto, nmFuncao, tipoDados, tipoRegistro, arquivoReferenciado, tipoFuncao) VALUES(?, ?, ?, ?, ?, ?, ?)";
    // Selects
    private static final String queryConsultaProjetos = "SELECT idProjeto, nmProjeto FROM Projeto";
    private static final String queryConsultaProjeto = "SELECT idProjeto, nmProjeto, pontoFuncao from Projeto where idProjeto = ?";
    private static final String queryConsultaFuncao = "SELECT idFuncao, idProjeto, nmFuncao, tipoDados, tipoRegistro, arquivoReferenciado, tipoFuncao from Funcao where idProjeto = ?";
    private static final String queryConsultaCaracteristicas = "SELECT idProjeto, idCaracteristicaGeral, nivelInfluencia from ProjetoHasCaracteristica where idProjeto = ?";
    // Deletes
    private static final String queryDeleteFuncoes = "DELETE from Funcao where idProjeto = ?";
    private static final String queryDeleteCaracteristicas = "DELETE from ProjetoHasCaracteristica where idProjeto = ?";
    private static final String queryDeleteProjeto = "delete from Projeto where idProjeto = ?";

    /**
     * Inclui um novo projeto no banco da dados.
     * @param projeto
     * @throws Exception
     */
    public static void incluirNovoProjeto(Projeto projeto) throws Exception {
        Connection con = null;
        try {
            con = DBUtils.getConnection();
            con.setAutoCommit(false);

            // Incluir projeto
            PreparedStatement stProjeto = con.prepareStatement(queryInsertProjeto, Statement.RETURN_GENERATED_KEYS);
            stProjeto.setInt(1, projeto.getId());
            stProjeto.setString(2, projeto.getNome());
            stProjeto.setDouble(3, projeto.getPontoFuncao());
            stProjeto.execute();

            // Recupera id auto-increment do projeto
            ResultSet rs = stProjeto.getGeneratedKeys();
            rs.next();
            projeto.setId(rs.getInt(1));

            // Incluir características
            Map<CaracteristicaGeral, NivelInfluencia> mapaCaracteristicas = projeto.getCaracteristicasGerais();
            Set<Entry<CaracteristicaGeral, NivelInfluencia>> setCaracteristicas = mapaCaracteristicas.entrySet();
            Iterator<Entry<CaracteristicaGeral, NivelInfluencia>> iteratorCaracteristicas = setCaracteristicas.iterator();
            while (iteratorCaracteristicas.hasNext()) {
                Entry<CaracteristicaGeral, NivelInfluencia> entry = iteratorCaracteristicas.next();
                CaracteristicaGeral caracteristica = entry.getKey();
                NivelInfluencia nivelInfluencia = entry.getValue();
                PreparedStatement stProjetoHasCaracteristica = con.prepareStatement(queryInsertProjetoHasCaracteristica);
                stProjetoHasCaracteristica.setInt(1, projeto.getId());
                stProjetoHasCaracteristica.setInt(2, caracteristica.getId());
                stProjetoHasCaracteristica.setInt(3, nivelInfluencia.getValor());
                stProjetoHasCaracteristica.execute();
            }

            // Incluir funções
            List<Funcao> listaFuncoes = projeto.getListaFuncoes();
            for (Funcao f : listaFuncoes) {
                PreparedStatement stFuncao = con.prepareStatement(queryInsertFuncao);
                stFuncao.setInt(1, f.getId());
                stFuncao.setInt(2, projeto.getId());
                stFuncao.setString(3, f.getNome());
                stFuncao.setInt(4, f.getTipoDados());
                if (f instanceof FuncaoDados) {
                    stFuncao.setInt(5, ((FuncaoDados) f).getTipoRegistro());
                    stFuncao.setInt(6, 0);
                } else if (f instanceof FuncaoTransacional) {
                    stFuncao.setInt(5, 0);
                    stFuncao.setInt(6, ((FuncaoTransacional) f).getArquivoReferenciado());
                }
                stFuncao.setInt(7, MyFPAUtil.getCodigoByTipoFuncao(f.getClass()));
                stFuncao.execute();
            }
            con.commit();
        } catch (Exception e) {
            e.printStackTrace();
            if (con != null) {
                try {
                    con.rollback();
                } catch (SQLException ex) {
                    ex.printStackTrace();
                    throw ex;
                }
            }
        } finally {
            if (con != null) {
                try {
                    con.close();
                } catch (SQLException ex) {
                    ex.printStackTrace();
                    throw ex;
                }
            }
        }
    }

    /**
     * Retorna uma lista com todos os projetos cadastrados
     * @return
     * @throws Exception
     */
    public static List<Projeto> consultaProjetos() {
        Connection con = null;
        try {
            con = DBUtils.getConnection();
            ResultSet rs = con.prepareCall(queryConsultaProjetos).executeQuery();
            List<Projeto> listaProjetos = new LinkedList<Projeto>();
            while (rs.next()) {
                Projeto projeto = new Projeto();
                projeto.setId(rs.getInt(1));
                projeto.setNome(rs.getString(2));
                listaProjetos.add(projeto);
            }
            return listaProjetos;
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (con != null) {
                try {
                    con.close();
                } catch (SQLException ex) {
                    Logger.getLogger(Persistence.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
        }
        return null;
    }

    /**
     * Retorna os dados de um projeto pelo seu ID
     * @param idProjeto
     * @return
     * @throws Exception
     */
    public static Projeto consultaProjeto(Integer idProjeto) throws Exception {
        Connection con = null;
        try {
            Projeto projeto = new Projeto();
            con = DBUtils.getConnection();

            // Consulta o projeto
            PreparedStatement stProjeto = con.prepareStatement(queryConsultaProjeto);
            stProjeto.setInt(1, idProjeto);
            ResultSet rsProjeto = stProjeto.executeQuery();
            while (rsProjeto.next()) {
                projeto.setId(rsProjeto.getInt(1));
                projeto.setNome(rsProjeto.getString(2));
                projeto.setPontoFuncao(rsProjeto.getDouble(3));
            }

            // Características
            PreparedStatement stCarac = con.prepareStatement(queryConsultaCaracteristicas);
            stCarac.setInt(1, idProjeto);
            ResultSet rsCarac = stCarac.executeQuery();
            Map<CaracteristicaGeral, NivelInfluencia> mapCarac = new HashMap<CaracteristicaGeral, NivelInfluencia>();
            while (rsCarac.next()) {
                int idCarac = rsCarac.getInt(2);
                int idNi = rsCarac.getInt(3);
                mapCarac.put(EnumerationUtil.getCaracteristicaGeralByValue(idCarac), EnumerationUtil.getNivelInfluenciaByValue(idNi));
            }
            projeto.setCaracteristicasGerais(mapCarac);

            // Consulta das funções
            PreparedStatement stFuncoes = con.prepareStatement(queryConsultaFuncao);
            stFuncoes.setInt(1, idProjeto);
            ResultSet rsFuncoes = stFuncoes.executeQuery();
            List<Funcao> listaFuncoes = new LinkedList<Funcao>();
            while (rsFuncoes.next()) {
                switch (rsFuncoes.getInt(7)) {
                    case 1:
                        ArquivoInterfaceExterna aie = new ArquivoInterfaceExterna();
                        aie.setId(rsFuncoes.getInt(1));
                        aie.setNome(rsFuncoes.getString(3));
                        aie.setTipoDados(rsFuncoes.getInt(4));
                        aie.setTipoRegistro(rsFuncoes.getInt(5));
                        listaFuncoes.add(aie);
                        break;
                    case 2:
                        ArquivoLogicoInterno ali = new ArquivoLogicoInterno();
                        ali.setId(rsFuncoes.getInt(1));
                        ali.setNome(rsFuncoes.getString(3));
                        ali.setTipoDados(rsFuncoes.getInt(4));
                        ali.setTipoRegistro(rsFuncoes.getInt(5));
                        listaFuncoes.add(ali);
                        break;
                    case 3:
                        SaidaExterna se = new SaidaExterna();
                        se.setId(rsFuncoes.getInt(1));
                        se.setNome(rsFuncoes.getString(3));
                        se.setTipoDados(rsFuncoes.getInt(4));
                        se.setArquivoReferenciado(rsFuncoes.getInt(6));
                        listaFuncoes.add(se);
                        break;
                    case 4:
                        ConsultaExterna ce = new ConsultaExterna();
                        ce.setId(rsFuncoes.getInt(1));
                        ce.setNome(rsFuncoes.getString(3));
                        ce.setTipoDados(rsFuncoes.getInt(4));
                        ce.setArquivoReferenciado(rsFuncoes.getInt(6));
                        listaFuncoes.add(ce);
                        break;
                    case 5:
                        EntradaExterna ee = new EntradaExterna();
                        ee.setId(rsFuncoes.getInt(1));
                        ee.setNome(rsFuncoes.getString(3));
                        ee.setTipoDados(rsFuncoes.getInt(4));
                        ee.setArquivoReferenciado(rsFuncoes.getInt(6));
                        listaFuncoes.add(ee);
                        break;
                    default:
                        return null;
                }
            }
            projeto.setListaFuncoes(listaFuncoes);
            return projeto;
        } catch (Exception e) {
            e.printStackTrace();
            throw e;
        } finally {
            if (con != null) {
                con.close();
            }
        }


    }

    /**
     * bla
     * @param idProjeto
     * @throws Exception
     */
    public static void exluirProjeto(int idProjeto) throws Exception {
        Connection con = null;
        try {
            con = DBUtils.getConnection();
            con.setAutoCommit(false);
            PreparedStatement stFuncoes = con.prepareStatement(queryDeleteFuncoes);
            stFuncoes.setInt(1, idProjeto);
            stFuncoes.executeUpdate();
            PreparedStatement stCarac = con.prepareStatement(queryDeleteCaracteristicas);
            stCarac.setInt(1, idProjeto);
            stCarac.executeUpdate();
            PreparedStatement stProj = con.prepareStatement(queryDeleteProjeto);
            stProj.setInt(1, idProjeto);
            stProj.executeUpdate();
            con.commit();
            con.close();
        } catch (Exception ex) {
            if (con != null) {
                con.rollback();
            }
            throw ex;
        }

    }
}
